
#include <arpa/inet.h>
#include "auth.h"
//#include "nacStationDB.h"
//#include "typeData/Typedata_station_coa.h"

//#include "db.h"
#include "log.h"
#include "devid.h"
#include "mqtt.h"

#include "session.h"

static int NACRadiusHandleDisconnectRequest(radmsg_t *msg);
static int NACRadiusHandleCOARequest(radmsg_t *msg);

/*
 *  接收DM 下线报文
 */
void RadDMServerCallBack(int fd, short event, void *arg) {
    int rc = 0;
    radmsg_t msg;
    char send_ip[16] = {0};

    memset(&msg, 0, sizeof (msg));
    if (!G_auth.radDM) {
        DEBUG("radius dm error");
        return;
    }
    // 接收消息
    if (radclt_recv(G_auth.radDM, &msg)) {
        DEBUG("Received DM message error");
        return;
    }
    inet_ntop(AF_INET, &(msg.from.sin_addr), send_ip, 16);
    DEBUG("Received radius message from host %s port %u length %d bytes,"
            "msg_code=%u[%d:DM; %d:COA]", send_ip, ntohs(msg.from.sin_port), msg.len,
            msg.code, RAD_DISCONNECT_REQUEST, RAD_COA_REQUEST);
    // 验证密钥
    if (radmsg_is_valid_req(G_auth.radDM, &msg)) {
        DEBUG("Received radius message from host %s port %u length %d bytes,"
                "msg_code=%u[%d:DM; %d:COA], authenticator error, secret is %s", send_ip, ntohs(msg.from.sin_port), msg.len,
                msg.code, RAD_DISCONNECT_REQUEST, RAD_COA_REQUEST, G_auth.secret);
        return;
    }
    switch (msg.code) {
        case RAD_DISCONNECT_REQUEST:
            if (!NACRadiusHandleDisconnectRequest(&msg)) {
                //radcliDMACKRep(G_auth.radDM, &msg, &msg.from);
                DEBUG("handle radius message from host %s port %u msg_code=%u[%d:DM; %d:COA] success",
                        send_ip, ntohs(msg.from.sin_port), msg.code, RAD_DISCONNECT_REQUEST, RAD_COA_REQUEST);
            } else {
                radcliDMNAKRep(G_auth.radDM, &msg, &msg.from);
                DEBUG("handle radius message from host %s port %u msg_code=%u[%d:DM; %d:COA] failed",
                        send_ip, ntohs(msg.from.sin_port), msg.code, RAD_DISCONNECT_REQUEST, RAD_COA_REQUEST);
            }
            break;
        case RAD_COA_REQUEST:
            if (NACRadiusHandleCOARequest(&msg) == 0) {
                //radcliCOAACKRep(G_auth.radDM, &msg, &msg.from);
                DEBUG("handle radius message from host %s port %u msg_code=%u[%d:DM; %d:COA] success",
                        send_ip, ntohs(msg.from.sin_port), msg.code, RAD_DISCONNECT_REQUEST, RAD_COA_REQUEST);
            } else {
                radcliCOANAKRep(G_auth.radDM, &msg, &msg.from);
                DEBUG("handle radius message from host %s port %u msg_code=%u[%d:DM; %d:COA] failed",
                        send_ip, ntohs(msg.from.sin_port), msg.code, RAD_DISCONNECT_REQUEST, RAD_COA_REQUEST);
            }
            break;
        default:
            DEBUG("not support radius message from %s:%d msg_code=%u",
                    send_ip, ntohs(msg.from.sin_port), msg.code);
            break;
    }


    return;
}

static int NACRadiusHandleDisconnectRequest(radmsg_t *msg) {
    int rc = 0;
    //  int len = 0;
    //int type = 0;
    //  uint8_t *p = NULL;
    //void *value = NULL;
    char nasip[32] = {'\0'};
    uint32_t bflow = 0;
    uint32_t gflow = 0;
    uint32_t inspeed = 0;
    uint32_t outspeed = 0;
    uint32_t sesstime = 0;
    char username[NAME_SIZE] = {0};
    char acctid[1024] = {0};


    uint32_t userip = 0;
    // char sessidstr[129] = {0};

    radven_t radattr;

    void* buf = malloc(sizeof (radmsg_t));
    memcpy(buf, (void*) msg, sizeof (radmsg_t));


    memset(&radattr, 0, sizeof (radven_t));


    if (nacRadiusAuthMSGParse(msg, &sesstime, &outspeed, &inspeed, &gflow, &bflow, username, acctid)) {
        DEBUG("parse radius COA attribute error");
        free(buf);
        return -1;
    }


#if 0
    for (;;) {
        type = radmsg_get_attr(msg, &value, &len);
        if (type < 1 || !value)
            break;
        switch (type) {
            case RAD_ACCT_SESSION_ID:
                if (len > 128) {
                    rc = -1;
                    DEBUG("ACCT-SESSION-Id length=%d > 128", len);
                } else
                    memcpy(sessidstr, value, len);
                break;
            case RAD_FRAMED_IP_ADDRESS:
                memcpy(&userip, value, 4);
                break;
            case RAD_USER_NAME:
                if (len > 128) {
                    DEBUG("User-Name length=%d > 128", len);

                } else {
                    memcpy(username, value, len);
                }
                break;
            default:
                DEBUG("Unknown attribute type=%d, length=%d", type, len);
                break;
        }
    }
#endif
    nacDMrequsetMSGdebug(username, acctid, userip);

    /*	查找终端  */
    //nac_user_lock();
    rc = find_session(username, acctid, nasip);
    //nac_user_unlock();
    if (rc < 0) {
        DEBUG("not found station [acct-id:%s, User-Name:%s]", acctid, username);
        free(buf);
        return -1;
    }
    DEBUG("Found nasip %s", nasip);

    pub_coa_msg(nasip, sizeof ( radmsg_t), buf, DISCONNECTION_MSG);
    free(buf);

    return rc;
}

static int NACRadiusHandleCOARequest(radmsg_t *msg) {
    int rc = 0;
    void* buf = malloc(sizeof (radmsg_t));
    char nasip[32] = {'\0'};
    uint32_t bflow = 0;
    uint32_t gflow = 0;
    uint32_t inspeed = 0;
    uint32_t outspeed = 0;
    uint32_t sesstime = 0;
    char username[NAME_SIZE] = {0};
    char acctid[1024] = {0};
    memcpy(buf, (void*) msg, sizeof (radmsg_t));

    if (nacRadiusAuthMSGParse(msg, &sesstime, &outspeed, &inspeed, &gflow, &bflow, username, acctid)) {
        DEBUG("parse radius COA attribute error");
        free(buf);
        return -1;
    }
    DEBUG("Receive packet:\n"
            "	User-Name = \"%s\"\n"
            "	Session-Timeout = %u\n"
            "	Rate-Input = %u\n"
            "	Rate-Output = %u\n"
            "	Max-Giga-Flow = %u\n"
            "	Max-Flow = %u"
            , username, sesstime, inspeed, outspeed, gflow, bflow, acctid
            );
    /*	查找终端  */
    //nac_user_lock();
    rc = find_session(username, acctid, nasip);
    //nac_user_unlock();
    if (rc < 0) {
        DEBUG("not found station [acct-id:%s, User-Name:%s]", acctid, username);
        free(buf);
        return -1;
    }
    DEBUG("Found nasip %s", nasip);
    //printf("%d\t%d\n", msg->len, msg->off);
    
    pub_coa_msg(nasip, sizeof (radmsg_t), buf, COA_MSG);
    free(buf);
    /*	获取授权信息  */

    DEBUG("station COA reply : %d", rc);
    return rc;
}

int nacRadiusAuthMSGParse(radmsg_t *msg, uint32_t *sesstime, uint32_t* outspeed,
        uint32_t *inspeed, uint32_t *gflow, uint32_t *bflow, char *username, char *sessidstr) {
    radven_t radattr;
    uint32_t vendor_id = 0;
    uint8_t *p1 = NULL;
    memset(&radattr, 0, sizeof (radven_t));

    if (!msg || !username) {
        DEBUG("radius parse input error.");
        return -1;
    }
    for (;;) {// 获取radius下发信息
        void *value = NULL;
        int len = 0;
        int type = radmsg_get_attr(msg, &value, &len);
        if (type < 1 || !value) {
            break;
        }
        DEBUG("Server attribute type %d,length %d bytes", type, len);
        switch (type) {
            case RAD_SESSION_TIMEOUT:
                if (len != 4) {
                    DEBUG("NAS get STD attribute type %d,length %d error", RAD_SESSION_TIMEOUT, len);
                } else {
                    uint8_t *p = NULL;
                    p = (uint8_t *) value;
                    *sesstime = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
                }
                break;
            case RAD_USER_NAME: //获取用户名
                if (len > 0) {
                    memcpy(username, value, len);
                } else
                    DEBUG("NAS get STD attribute type %d,length %d error", RAD_USER_NAME, len);
                break;
            case RAD_ACCT_SESSION_ID:
                if (len > 128) {
                    DEBUG("ACCT-SESSION-Id length=%d > 128", len);
                } else
                    memcpy(sessidstr, value, len);
                break;
            case RAD_VENDOR_SPECIFIC:

                p1 = (uint8_t *) value;
                vendor_id = p1[0] << 24 | p1[1] << 16 | p1[2] << 8 | p1[3];

                if (vendor_id != 0 && vendor_id != MY_VENDOR_ID) {

                    break;
                }
                //xyz_log_debug("+++++++++++= vendor id =%u",vendor_id);
                memset(&radattr, 0, sizeof (radven_t));
                DEBUG("Vendor attribute length %d bytes", len);
                type = radmsg_get_vendor_attr((uint8_t *) value, len, &radattr);

                switch (type) {
                    case RAD_RATE_DOWN:
                        if (4 == radattr.len && radattr.value) {
                            *inspeed = radattr.value[0] << 24 | radattr.value[1] << 16
                                    | radattr.value[2] << 8 | radattr.value[3];
                        }
                        break;
                    case RAD_RATE_UP:
                        if (4 == radattr.len && radattr.value) {
                            *outspeed = radattr.value[0] << 24 | radattr.value[1] << 16
                                    | radattr.value[2] << 8 | radattr.value[3];
                        }
                        break;
                    case RAD_MAX_FLOW:
                        if (4 == radattr.len && radattr.value) {
                            *bflow = radattr.value[0] << 24 | radattr.value[1] << 16
                                    | radattr.value[2] << 8 | radattr.value[3];
                        }
                        break;
                    case RAD_MAX_GIGA_FLOW:
                        if (4 == radattr.len && radattr.value) {
                            *gflow = radattr.value[0] << 24 | radattr.value[1] << 16
                                    | radattr.value[2] << 8 | radattr.value[3];
                        }
                        break;

                    default:
                        DEBUG("NAS Get STD Attribute type %d error", type);
                        break;
                }
                break;
        }
    }
    return 0;
}